const CFG = require('../config');
const FUN = require('../comm/func');
const rCache = require('../model/redis_cache');
const DB = require('../model/model_data');
const dbHelp = require('../comm/db-helper');
const timLib = require('../comm/time-lib');
const mbLib = require('../comm/modbus-lib');
const PMR = require('../utils/policyManger');
const dataCalc = require('../comm/data-calc');

module.exports = class Mb_Cnt
{
  //将数据库选项缓存到redis
  //policy 来自数据库的记录
  //opts 数据库json字段的解析对象
  //CacheCallback 存储执行函数
  static InitCache(policy,opts,CacheCallback)
  {
    let obj=PMR.baseParam(policy);//即将缓存到redis的参数

    //检查json选项
    if( parseInt(policy.record_limit)==0 ||
        parseInt(opts.fetch_period)==0 )
    {
      console.error('wrong mb-cnt params',policy.id);
      return false;
    }

    //json options参数存储
    obj.fetch_period = opts.fetch_period;//采样周期
    obj.cache_expire = Number(opts.fetch_period)+60;//数据缓存reids有效期
    obj.record_limit = policy.record_limit;//数据库条数上限
    obj.record_duration = 0;//数据库周期上限

    //数据库处理
    dbHelp.createTab(obj.table,policy.uid);

    //缓存主站参数
    obj.hwid = policy.master;
    obj.role = 'master';
    obj.brother = policy.brother;
    CacheCallback(policy.id,policy.master,obj);//记录到缓存

    //缓存从站参数
    if(Number(policy.brother) && policy.brother!=-1)
    {
      obj.hwid = policy.brother;
      obj.role = 'slave';
      obj.brother = policy.master;
      CacheCallback(policy.id,obj.hwid,obj);//记录到缓存
    }
  }//end InitCache


  //modbus数据总分析
  // topic = {project,devType,func,devId}
  // dConf即InitCache中的obj
  // dataBuf即mq消息内容，buf对象
  static async DataIn(topic, dConf, dataBuf)
  {
    mbLib.mbPacketParse(topic.func, dConf, dataBuf, (last,sConf)=>{
      this.dataHandler(last,sConf,dataBuf,timLib.NowStamp());
    })
  }//end function DataIn


  //普通的数据处理,每隔一段时间就求平均值入数据库
  static dataHandler(last,dConf,data,stamp)
  {
    let preStamp=timLib.Rounding(dConf.fetch_period,stamp);
    let nodeDevStamp=PMR.rdNode(dConf)+':'+preStamp;
    let nodeDevAddr=nodeDevStamp+':'+last.addr;

    //处理前一个数据，为提高效率，此处不能用await
    rCache.get(nodeDevStamp,'addrs',last.addr).then((val)=>{
      if(val!=last.addr) //不存在,说明现在是第一个数据,那么可以处理旧数据
        this.prevDataHandler(PMR.rdNode(dConf),dConf,stamp);
    })

    //计数
    rCache.incKey(nodeDevAddr,'count',dConf.cache_expire);

    //记地址
    let obj={};
    obj[last.addr]=last.addr;
    rCache.set(nodeDevStamp,'addrs',obj,dConf.cache_expire);

    //记数值
    let modObj={};
    for(let i=0;i<last.regNum;i++)
    {
      let regVal=(Number(data[3+i*2])<<8)+Number(data[4+i*2]);
      if(modObj['val.'+regVal]) modObj['val.'+regVal]+=1;
      else modObj['val.'+regVal]=1;
    }

    //逐一记录到缓存，没法在前面的循环里做，因为存储过程会异步导致记录不准确
    Object.keys(modObj).some((key)=>{
      rCache.hasKey(nodeDevAddr,key).then((has)=>{
        if(has) rCache.incKey(nodeDevAddr,key,dConf.cache_expire,modObj[key]);
        else rCache.setKey(nodeDevAddr,key,modObj[key],dConf.cache_expire);
      })
    })//end some
  }//end funciton

  //处理前一个时间段的数据
  static async prevDataHandler(nodeDev,dConf,stamp)
  {
    let preStamp=timLib.PrevRounding(dConf.fetch_period);//获取前一段的时间戳
    let nodeStamp=nodeDev+':'+preStamp;

    let db_stamp=await rCache.getKey(nodeDev,'db_stamp');
    if(db_stamp!=null && stamp-db_stamp<CFG.DATA_SAVE_GAP)//存储到数据库的频率不能过快
    {pcyLog('[%d] mdata fast(%dmS)',dConf.policy_id,stamp-db_stamp,timLib.NowStr());return;}

    let addrs=await rCache.getAll(nodeStamp,'addrs');
    for(let addr in addrs)//每个地址的都找出来
    {
      let nodeStampAddr=nodeStamp+':'+addr;
      let count=await rCache.getKey(nodeStampAddr,'count');
      if(count==null || count==0) return;//数据已经处理过了

      let nodes=await rCache.keys(nodeStampAddr,'val.*');
      if(nodes==null){pcyLog('[%d] not cache record',dConf.hwid);return;}

      let save={cnt:parseInt(count)};
      for(let i=0;i<nodes.length;i++)//轮询每个数值项
      {
        let val=nodes[i].match(/[0-9]{1,}$/g);//获取变量名
        let valCnt=await rCache.getKey(nodes[i],'');//获取变量次数
        save[val]=Number(valCnt);
      }

      //输出到数据库
      DB.newRcd(dConf.uid, dConf.table, 'hwid,policy_id,addr,json,stamp',
                [dConf.hwid, dConf.policy_id, addr, JSON.stringify(save), preStamp]);
      rCache.setKey(nodeDev,'db_stamp',stamp,60);//记录数据库访问时间戳
      PMR.policyInform('update',dConf);
      pcyLog('[%d][%d] ValCnt addr%d(x%d)',dConf.policy_id,dConf.hwid,addr,count,preStamp,timLib.Stamp2Str(preStamp));

      //删除缓存中的使用过的数据
      rCache.del(nodeStamp,'addrs');
    }//end let addr

    //删除数据库中冗余的前期数据
    dataCalc.dbRecordTidy(dConf.uid,dConf.table,dConf.policy_id,dConf.record_limit);
    if(dConf.out && dConf.calc && dConf.out_tab)
      dataCalc.dbRecordTidy(dConf.uid,dConf.out_tab,dConf.policy_id,dConf.record_limit);

  }//end function

} //end class Modbus